package co.gov.medellin.sdep.sisdep.integration.mapasmedellin;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import javax.faces.model.SelectItem;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

/**
 * Clase Cliente que permite gestionar la invocacion al Servicio WEB de GeoUbicacion de la Alcaldia de Medellin,
 * para la consulta de Comunas, Barrios por Comuna y la GeoUbicacion de una Direccion (texto plano). 
 * 
 * @author carlos.perez@consult-soft.com
 * @version 30-04-2012 
 */
public class GeoUbicacionClient implements java.io.Serializable {
	
	/** Serial key. */
	private static final long serialVersionUID = -1184759686969700426L;
	
	private String endPoint;
	private CacheManager cacheManager;
	private Cache mlCache = null;
	
	private static final String COMUNAS_LIST = "COMUNAS_CACHE_LIST";
	private static final String BARRIOS_LIST = "BARRIOS_CACHE_LIST";
	
	// Se definen los objetos para inializar el servicio WEB:
	private GeoUbicacionWS_Service geoUbicacionWS;
	private GeoUbicacionWS_PortType geoUbicacionPort;
		
	/**
	 * Metodo que inicializa el Spring Bean correspondiente al CacheManager.
	 * 
	 * @param cacheManager
	 */
	public void setCacheManager(CacheManager cacheManager) {
		this.cacheManager = cacheManager;
	}

	
	/**
	 * Metodo que realiza la inicializacion de la lista de Comunas y Barrios definidos por la Alcaldia
	 * de Medellin, y los ubica en el cache de la aplicacion WEB. Este metodo se debe usar en el 
	 * en el portal SAP de la Alcaldia pues hace uso del Servicio WEB de Mapas Medellin que NO esta
	 * disponible localmente en un entorno de DLLO.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 30-04-2012 
	 */
	private void setupWSClient()  {
		
		if (null == mlCache) {
			mlCache = cacheManager.getCache("masterListCache");
		}
		try {
			// Se inicializa el servicio y su endpoint para invocarlo:
			geoUbicacionWS = new GeoUbicacionWS_ServiceLocator(getEndPoint());
	    	geoUbicacionPort = geoUbicacionWS.getGeoUbicacionWSPort();
	    	
	    	BeanComunaCorregimiento[] comunasArray = geoUbicacionPort.getComunaCorregimiento();
			
			List comunasList = new ArrayList(comunasArray.length);
			List barriosList = new ArrayList(comunasArray.length * 100);
			for (int i = 0; i < comunasArray.length; i++) {
				comunasList.add(comunasArray[i]);
				
				BeanBarrioVereda[] barriosArray = geoUbicacionPort.getBarrioVereda(comunasArray[i].getCodCodigo());
				for (int j = 0; j < barriosArray.length; j++) {
					barriosList.add(barriosArray[j]);
				}				
			}
			Collections.sort(comunasList, COMUNA_ALPHABETIC_ORDER);
			mlCache.put(new Element(COMUNAS_LIST, getComunasSelectOneMenuList(comunasList)));
			
			Collections.sort(barriosList, BARRIO_ALPHABETIC_ORDER);
			mlCache.put(new Element(BARRIOS_LIST, getBarriosSelectOneMenuList(barriosList)));
			
		} catch(Exception e) {
			System.err.println("SISDEP::GeoUbicacionClient.setupWSClient-->");
			e.printStackTrace(System.err);
			
			// Inicializa los listados por defecto en caso de error al inicializar el servicio:
			setupWSClientDefault();
		}
	}
	
	/**
	 * Metodo que realiza la inicializacion por DEFECTO de la lista de Comunas y Barrios, 
	 * y los ubica en el cache de la aplicacion WEB. Este metodo se debe usar en el 
	 * entorno de DLLO local con el fin de realizar la implementacion de los modulos que
	 * necesiten Comuna o Barrio.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 02-05-2012
	 *  
	 */ 
	private void setupWSClientDefault() {
		geoUbicacionPort = null;
		
		if (null == mlCache) {
			mlCache = cacheManager.getCache("masterListCache");
		}
		// Inicializa un listado de comunas por defecto:
		BeanComunaCorregimiento cocoTO = new BeanComunaCorregimiento("-1", "-- SIN COMUNAS --");
		List comunasEmptyList = new ArrayList(1);
		comunasEmptyList.add(cocoTO);
		mlCache.put(new Element(COMUNAS_LIST, getComunasSelectOneMenuList(comunasEmptyList)));
		
		// Inicializa un listado de barrios por defecto:
		BeanBarrioVereda baveTO = new BeanBarrioVereda("-1", "-- SIN BARRIOS --");
		List barriosEmptyList = new ArrayList(1);
		barriosEmptyList.add(baveTO);
		mlCache.put(new Element(BARRIOS_LIST, getBarriosSelectOneMenuList(barriosEmptyList)));	
	}
	
	/**
	 * Metodo que permite obtener un listado de las COMUNAS registradas en la Alcaldia de Medellin.
	 * En caso de NO estar disponible el Servicio WEB de consulta, se retorna un valor por
	 * defecto inicializado en el cache.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 30-04-2012 
     *
	 * @return List<BeanBarrioVereda>
	 */
	public List getComunasList() {
		Element cachedObject = mlCache.get(COMUNAS_LIST);
		List comunasList = null;
		if (cachedObject != null) {
			comunasList = (List) cachedObject.getObjectValue();
		}
		return comunasList;
	}
	
	/**
	 * Metodo que permite obtener un Objeto con los datos de una Comuna basandose en su codigo.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 02-05-2012 
	 *
	 * @param code
	 * @return BeanComunaCorregimiento
	 */
	public BeanComunaCorregimiento getComunaByCode(String code) {
		BeanComunaCorregimiento cocoTO = null;

		Iterator comunaIter = getComunasList().iterator();
		while(comunaIter.hasNext() && (null == cocoTO)) {
			SelectItem comunaItem = (SelectItem) comunaIter.next();
			if(((String)comunaItem.getValue()).equals(code)) {
				cocoTO = new BeanComunaCorregimiento((String)comunaItem.getValue(), comunaItem.getLabel());
			}			
		}		
		return cocoTO;
	}
	
	/**
	 * Metodo que permite obtener un listado de los barrios listo para ser presentados en un
	 * elemento SelectOneMenu de RichFaces.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 30-04-2012 
 	 *
 	 * @param comunasList
	 * @return List SelectItem<BeanComunaCorregimiento>
	 */	
	private List getComunasSelectOneMenuList(List comunasList) {
		List comunasSelectOneMenuList = null;
		if(comunasList != null) {
			comunasSelectOneMenuList = new ArrayList(comunasList.size());
			Iterator comunasIter = comunasList.iterator();
			while (comunasIter.hasNext()) {
				BeanComunaCorregimiento cocoTO = (BeanComunaCorregimiento) comunasIter.next();
				comunasSelectOneMenuList.add(new SelectItem(cocoTO.getCodCodigo(), cocoTO.getValueNombre()));
			}
		}
		return comunasSelectOneMenuList;
	}

	/**
	 * Metodo que permite obtener un listado de los BARRIOS registrados en la Alcaldia de Medellin.
	 * En caso de NO estar disponible el Servicio WEB de consulta, se retorna un valor por
	 * defecto inicializado en el cache.
	 * 
	 * @return List<BeanBarrioVereda>
	 */
	public List getBarriosList() {
		Element cachedObject = mlCache.get(BARRIOS_LIST);
		List barriosList = null;
		if (cachedObject != null) {
			barriosList = (List) cachedObject.getObjectValue();
		}
		return barriosList;
	}

	/**
	 * Metodo que permite obtener un Objeto con los datos de un Barrio basandose en su codigo.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 02-05-2012
	 *  
	 * @param code
	 * @return BeanBarrioVereda
	 */
	public BeanBarrioVereda getBarrioByCode(String code) {
		BeanBarrioVereda baveTO = null;

		Iterator barrioIter = getBarriosList().iterator();
		while(barrioIter.hasNext() && (null == baveTO)) {
			SelectItem barrioItem = (SelectItem) barrioIter.next();
			if(((String)barrioItem.getValue()).equals(code)) {
				baveTO = new BeanBarrioVereda((String)barrioItem.getValue(), barrioItem.getLabel());
			}			
		}		
		return baveTO;
	}

	/**
	 * Metodo que permite obtener un listado de las comunas listo para ser presentados en un
	 * elemento SelectOneMenu de RichFaces.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 30-04-2012 
 	 *
 	 * @param barriosList
	 * @return List SelectItem<BeanBarrioVereda>
	 */
	private List getBarriosSelectOneMenuList(List barriosList) {
		List barriosSelectOneMenuList = null;
		if(barriosList != null) {
			barriosSelectOneMenuList = new ArrayList(barriosList.size());
			Iterator barriosIter = barriosList.iterator();
			while (barriosIter.hasNext()) {
				BeanBarrioVereda baveTO = (BeanBarrioVereda) barriosIter.next();
				barriosSelectOneMenuList.add(new SelectItem(baveTO.getCodCodigo(), baveTO.getValueNombre()));
			}
		}
		return barriosSelectOneMenuList;
	}

	/**
	 * Metodo que permite consultar los datos de una direccion especificada por medio de un texto plano.
	 * Para ello se invoca al Servicio WEB de GeoUbicacion de la Mapas Medellin.
	 * NOTA: En caso de NO ser localizada la Direccion, el Servicio WEB retorna un valor de cero (0)
	 * en sus coordenadas X, Y.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 30-04-2012 
	 *
	 * @param address
	 * @return BeanDireccion
	 */
	public BeanDireccion getAddressLocalization(String address) {
		BeanDireccion addressTO = null;
		
		// Se verifica el acceso al endPoint de servicio WEB:
		if(geoUbicacionPort != null) {
			try {
		    	addressTO = geoUbicacionPort.getUbicaDirUrbana(address);
			} catch(Exception e) {
				System.err.println("SISDEP::GeoUbicacionClient.getAddressLocalization(address)-->" + address);
				e.printStackTrace(System.err);			
			}
		}
		return addressTO;
	}

	/**
	 * Metodo que permite consultar los datos de una direccion especificada por medio de un texto plano.
	 * Para ello se invoca al Servicio WEB de GeoUbicacion de la Mapas Medellin.
	 * Especialmente es utilizado para obtener la URL de la imagen en el servidor de MapasMedellin
	 * para obtener la imagen.
	 * NOTA: En caso de NO ser localizada la Direccion, el Servicio WEB retorna un valor de cero (0)
	 * en sus coordenadas X, Y.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 14-06-2012 
	 *
	 * @param address
	 * @return BeanDireccion
	 */
	public BeanDireccion getAddressImageURL(String address) {
		BeanDireccion addressTO = null;
		
		// Se verifica el acceso al endPoint de servicio WEB:
		if(geoUbicacionPort != null) {
			try {
				addressTO = geoUbicacionPort.getUbicaDirUrbanaImagen(address, 400, 300);
			} catch(Exception e) {
				System.err.println("SISDEP::GeoUbicacionClient.getAddressURLImage(address)-->" + address);
				e.printStackTrace(System.err);			
			}
		}
		return addressTO;
	}
	
	/**
	 * @return
	 */
	public String getEndPoint() {
		return endPoint;
	}

	/**
	 * @param string
	 */
	public void setEndPoint(String string) {
		endPoint = string;
	}

	/** 
	 * Objeto comparador para ser usado en el ordenamiento de las listas de comunas 
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 30/04/2012  
	 */
	private static final Comparator COMUNA_ALPHABETIC_ORDER = new Comparator() {
	    public int compare(Object cbm1,	Object cbm2) {
	        return ((BeanComunaCorregimiento)cbm1).getValueNombre().compareTo(((BeanComunaCorregimiento)cbm2).getValueNombre());
	    }
	};

	/** 
	 * Objeto comparador para ser usado en el ordenamiento de las listas de barrios 
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 30/04/2012  
	 */
	private static final Comparator BARRIO_ALPHABETIC_ORDER = new Comparator() {
	    public int compare(Object cbm1,	Object cbm2) {
	        return ((BeanBarrioVereda)cbm1).getValueNombre().compareTo(((BeanBarrioVereda)cbm2).getValueNombre());
	    }
	};

}
